﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoA.Shared.Interfaces;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace LoA.Shared.Physics {
	public class PhysicalBody : IPhysicalBody {
		/// <summary>
		/// Model obiektu
		/// </summary>
		protected readonly Model bodyVisualModel;
		protected readonly PhysicalModel bodyModel;
		protected PhysicalState bodyState;

		public PhysicalBody(Model bodyVisualModel, Vector3 position, Matrix orientation) {
			this.bodyVisualModel = bodyVisualModel;
			this.bodyModel = new PhysicalModel( );
			this.bodyState = new PhysicalState(this.bodyModel);
			this.bodyState.Position = position;
			this.bodyState.Orientation = orientation;
		}

		public PhysicalBody(Model bodyVisualModel, PhysicalModel physicalModel, Vector3 position, Matrix orientation) {
			this.bodyVisualModel = bodyVisualModel;
			this.bodyModel = physicalModel;
			this.bodyState = new PhysicalState(this.bodyModel);
			this.bodyState.Position = position;
			this.bodyState.Orientation = orientation;
		}

		public PhysicalBody(Model bodyVisualModel, Vector3 position, Matrix orientation, AnimationStructure anim) {
			this.bodyVisualModel = bodyVisualModel;
			this.bodyModel = new PhysicalModel( );
			this.bodyState = new PhysicalState(this.bodyModel);
			this.bodyState.Position = position;
			this.bodyState.Orientation = orientation;
			this.anim = anim;
		}

		protected AnimationStructure anim;

		public AnimationStructure Animation { get { return anim; } }

		/// <summary>
		/// Model of the Physical Body
		/// </summary>
		public Model BodyVisualModel { get { return this.bodyVisualModel; } }
		/// <summary>
		/// 
		/// </summary>
		public PhysicalModel BodyModel { get { return this.bodyModel; } }
		/// <summary>
		/// 
		/// </summary>
		public PhysicalState BodyState { get { return this.bodyState; } }

		#region 3D Visual Model Methods
		/// <summary>
		/// Checks whether the current BoundingSphere intersects with a specified BoundingSphere.
		/// </summary>
		/// <param name="phObject">The BoundingSphere to check for intersection with the current BoundingSphere.</param>
		/// <returns>bool</returns>
		public bool Intersects(IPhysicalBody physicalBody) {
			bool result = false;
			foreach (var mesh1 in bodyVisualModel.Meshes) {
				foreach (var mesh2 in physicalBody.BodyVisualModel.Meshes) {
					BoundingSphere bs1 = mesh1.BoundingSphere;
					bs1.Center += this.BodyState.Position;
					BoundingSphere bs2 = mesh2.BoundingSphere;
					bs2.Center += physicalBody.BodyState.Position;
					result = bs1.Intersects(bs2);
					if (result == true)
						return true;
				}
			}
			return result;
		}

		/// <summary>
		/// Checks whether the current BoundingSphere contains the specified BoundingSphere.
		/// </summary>
		/// <param name="phObject">The BoundingSphere to check against the current BoundingSphere.</param>
		/// <returns></returns>
		public ContainmentType Contains(IPhysicalBody physicalBody) {
			ContainmentType result = ContainmentType.Disjoint;
			foreach (var mesh1 in bodyVisualModel.Meshes) {
				foreach (var mesh2 in physicalBody.BodyVisualModel.Meshes) {
					BoundingSphere bs1 = mesh1.BoundingSphere;
					bs1.Center += this.BodyState.Position;
					BoundingSphere bs2 = mesh2.BoundingSphere;
					bs2.Center += physicalBody.BodyState.Position;
					switch (bs1.Contains(bs2)) {
						case ContainmentType.Contains:
							result = ContainmentType.Contains;
							break;
						case ContainmentType.Intersects:
							if (result != ContainmentType.Contains)
								result = ContainmentType.Intersects;
							break;
					}
				}
			}
			return result;
		}
		#endregion 3D Visual Model Methods
		public void Hit(object sender, Vector3 direction, float force) {
			if (sender is IPhysics) {
				this.bodyState.Direction = direction;
				this.bodyState.Force = force;
			}
		}
		public void Move(object sender, Direction direction, float time) {
			Vector3 velocity = Vector3.Zero;
			switch (direction) {
				case Shared.Physics.Direction.Forward:
					velocity = this.bodyState.Orientation.Forward;
					break;
				case Shared.Physics.Direction.Backward:
					velocity = this.bodyState.Orientation.Backward;
					break;
				case Shared.Physics.Direction.Left:
					velocity = this.bodyState.Orientation.Left;
					break;
				case Shared.Physics.Direction.Right:
					velocity = this.bodyState.Orientation.Right;
					break;

			}
			this.bodyState.Position += velocity * time;
		}

		public void Turn(object sender, Rotation rotation, float time) {
			switch (rotation) {
				case Rotation.TurnLeft:
					this.bodyState.Orientation = Matrix.Transform(this.bodyState.Orientation, Quaternion.CreateFromYawPitchRoll(time, 0, 0));
					break;
				case Rotation.TurnRight:
					this.bodyState.Orientation = Matrix.Transform(this.bodyState.Orientation, Quaternion.CreateFromYawPitchRoll(-time, 0, 0));
					break;
				case Rotation.TurnUp | Rotation.TurnDown:

					break;

			}
			//this.Orientation = Matrix.Transform(this.Orientation, Quaternion.CreateFromYawPitchRoll(time, 0, 0));
			//throw new NotImplementedException( );
		}

		public void Update(GameTime gameTime) {
			this.anim.Update(gameTime);
		}

		public void Draw(ICamera camera) {

			this.BodyVisualModel.Draw(Matrix.CreateWorld(this.BodyState.Position, this.BodyState.Orientation.Forward, Vector3.Up), camera.ViewMatrix, camera.ProjectionMatrix);

			/*
			Matrix[] boneTransforms = null;

			Matrix rootTransform = Matrix.Identity;

			Matrix worldMatrix = Matrix.CreateWorld(this.bodyState.Position, this.bodyState.Orientation.Forward, this.bodyState.Orientation.Up);

			foreach (ModelMesh mesh in BodyVisualModel.Meshes) {
				if (mesh.Effects.Count > 0) {
					var firstEffect = mesh.Effects.First( );
					if (firstEffect is BasicEffect) {

						boneTransforms = new Matrix[this.BodyVisualModel.Bones.Count];

						this.BodyVisualModel.CopyAbsoluteBoneTransformsTo(boneTransforms);

						foreach (BasicEffect effect in mesh.Effects) {

							/*if (boneTransforms != null)
								//effect.SetBoneTransforms(boneTransforms);*/

							/*effect.World = boneTransforms[mesh.ParentBone.Index] * worldMatrix;
							effect.View = camera.ViewMatrix;
							effect.Projection = camera.ProjectionMatrix;

							camera.SetLighting(effect);
						}
					} else if (firstEffect is SkinnedEffect) {
						if (anim.roots != null && anim.roots.Length > 0)
							rootTransform = anim.root;

						if (anim.transforms != null && anim.transforms.Length > 0)
							boneTransforms = anim.transform;

						boneTransforms = new Matrix[this.BodyVisualModel.Bones.Count];
						this.BodyVisualModel.CopyAbsoluteBoneTransformsTo(boneTransforms);

						foreach (SkinnedEffect effect in mesh.Effects) {
							var bones = effect.GetBoneTransforms(boneTransforms.Length);

							if (boneTransforms != null)
								effect.SetBoneTransforms(boneTransforms);

							effect.World = rootTransform * worldMatrix;	
							effect.View = camera.ViewMatrix;
							effect.Projection = camera.ProjectionMatrix;

							//effect.SpecularColor = Vector3.Zero;
							//effect.EmissiveColor = new Vector3(2, 3, 4);
							
							camera.SetLighting(effect);
						}
					}
				}
				mesh.Draw( );
			}*/
		}

		#region Physics Engine Methods
		public void CorrectPosition(object sender, Vector3 position) {
			if (sender is IPhysics) {
				this.bodyState.Position = position;
			}
		}

		public void CorrectDirection(object sender, Vector3 direction) {
			if (sender is IPhysics) {
				this.bodyState.Direction = direction;
			}
		}

		public void CorrectForce(object sender, float force) {
			if (sender is IPhysics) {
				this.bodyState.Force = force;
			}
		}

		public void CorrectState(object sender) {
			if (sender is IPhysics) {

			}
		}
		#endregion Physics Engine Methods
	}
}
